package com.minelx.datamicroscope.querylanguage.data.sql;

import com.alibaba.fastjson.JSONObject;
import com.minelx.datamicroscope.querylanguage.data.Condition;

import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Stream;

import static com.minelx.datamicroscope.querylanguage.data.QueryBuilder.strip;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;

public class Conditions {
    public static final List<String> COMPARING_OPERATORS = Arrays.asList("lt", "le", "gt", "ge");

    private final List<ConditionProvider> inProviders;

    private final List<ConditionProvider> eqProviders;

    private final List<ConditionProvider> comparingProviders;

    private Conditions(List<ConditionProvider> inProviders,
                       List<ConditionProvider> eqProviders,
                       List<ConditionProvider> comparingProviders) {
        this.inProviders = inProviders;
        this.eqProviders = eqProviders;
        this.comparingProviders = comparingProviders;
    }

    public List<JSONObject> firstCollectionConditions() {
        List<Condition> inContents = inProviders.stream()
                .map(this::inProviderToContent)
                .collect(toList());

        List<Condition> eqContent = eqProvidersToContent(eqProviders);

        List<Condition> comparingContent = comparingProvidersToContent(comparingProviders);

        return Stream.of(inContents, eqContent, comparingContent)
                .flatMap(Collection::stream)
                .map(Condition::toJSON)
                .collect(toList());
    }

    private List<Condition> comparingProvidersToContent(List<ConditionProvider> comparingProviders) {
        return comparingProviders.stream()
                .map(ConditionProvider::toCondition)
                .collect(toList());
    }

    private List<Condition> eqProvidersToContent(List<ConditionProvider> eqProviders) {
        if (eqProviders.isEmpty()) {
            return emptyList();
        }
        return singletonList(Condition.content(renderToCSV(eqProviders)));
    }

    private Condition inProviderToContent(ConditionProvider inProvider) {
        List<String> rightValues = rightAsListS(inProvider.right());
        String contentAsCSV = inProvider.left().fieldName() + "\n" +
                String.join("\n", rightValues);
        return Condition.content(contentAsCSV);
    }

    String renderToCSV(List<ConditionProvider> conditions) {
        String title = conditions.stream()
                .map(conditionProvider -> conditionProvider.left().fieldName())
                .collect(joining(","));
        return title + "\n" + renderContent(conditions);
    }

    private String renderContent(List<ConditionProvider> conditions) {
        // FIXME 2022/3/8 wait for fixing.     support for single IN expr
        if (conditions.size() == 1) {
            return String.join("\n", rightAsListS(conditions.get(0).right()));
        }
        return conditions.stream()
                .map(literalCondition -> rightAsListS(literalCondition.right()))
                .map(items -> String.join(",", items))
                .collect(joining(","));
    }

    public static Conditions create(List<ConditionProvider> conditions) {
        List<ConditionProvider> in = conditions.stream()
                .filter(which -> which.literalKind().equals("in"))
                .collect(toList());
        List<ConditionProvider> eq = conditions.stream()
                .filter(which -> which.literalKind().equals("eq"))
                .collect(toList());
        List<ConditionProvider> comparing = conditions.stream()
                .filter(which -> COMPARING_OPERATORS.contains(which.literalKind()))
                .collect(toList());
        return new Conditions(in, eq, comparing);
    }

    public static List<String> rightAsListS(String text) {
        return Arrays.stream(text.split(","))
                .map(item -> strip(item, "'"))
                .collect(toList());
    }
}
